﻿using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using XfTechOAWeb.IService;
using XfTechOAWeb.Infrastructure.JWT;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using XfTechOAWeb.Dtos;
using XfTechOAWeb.Infrastructure.RedisCache;
using XfTechOAWeb.Infrastructure.Enums;
using XfTechOAWeb.Infrastructure.Extensions;
using System.Security.Claims;

using Microsoft.AspNetCore.Hosting; //引用这个包
using Microsoft.Extensions.Hosting;

namespace XfTechOAWeb.Controllers
{
    /// <summary>
    /// 用户账号管理
    /// </summary>
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class AccountController : ControllerBase
    {
        public IUserService _userService { get; set; } //依赖的是抽象的接口，所以符合依赖倒置原则
        private readonly ICSRedisHelper _redisClient;
        //private readonly JwtTokenHelper _jwtTokenHelper; //没有依赖一个接口，所以不符合依赖倒置原则

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="userService"></param>
        /// <param name="redisClient"></param>
        public AccountController(/*IUserService userService,*/
                                 ICSRedisHelper redisClient)
        {
            //_userService = userService;
            _redisClient = redisClient;
            //(_redisClient, _jwtTokenHelper) = (redisClient, jwtTokenHelper);
        }

        /// <summary>
        /// 用户登录
        /// </summary>
        /// <param name="userLogin"></param>
        /// <returns></returns>
        [HttpPost]
        //[Route("{Name}/{Password}")]      //Route路由传参就是用 /xxx/xxx，  Query请求参数 ?xxx=xx&xx=xxx
        [AllowAnonymous] //允许匿名访问的特性
        public IActionResult Login(UserLoginRequestDto userLogin,
                                   //[FromServices] IWebHostEnvironment env,
                                   [FromServices] JwtTokenHelper _jwtTokenHelper) //③方法注入依赖
        {
            //if(env.IsDevelopment())
            //{
            //    return Ok("开发环境！");
            //}

            //ASP.NET Core框架自动进行参数验证，   DTO的验证规则是我们的定的
            if (!ModelState.IsValid) //模型验证 ，ModelState是验证的结果
            {
                return BadRequest(); //请求有误 403
            }
            //返回结果
            UserLoginResponseDto res = new UserLoginResponseDto();

            var user = _userService.GetUserByNameAndPwd(userLogin);
            if (user != null)
            {
                //生成对应的声明
                List<Claim> claims= new List<Claim>();
                claims.Add(new Claim(ClaimTypes.NameIdentifier, user.UId.ToString()));
                claims.Add(new Claim(ClaimTypes.Name, user.Account));
                if(user.Account.ToLower() == "admin")
                {
                    claims.Add(new Claim(ClaimTypes.Role, "admin"));
                }
                else
                {
                    claims.Add(new Claim(ClaimTypes.Role, "users"));
                }

                //生成token
                res.TokenInfo = _jwtTokenHelper.CreateToken(claims);

                res.Code = (int)ResponseCode.Success;
                res.Msg = ResponseCode.Success.GetDescription();
                res.Data = user; //用户信息

                //生成RefreshToken
                res.RefreshToken = Guid.NewGuid().ToString(); //生成一个键值(全球唯一标识符，雪花ID)

                //保存到Redis里边
                _redisClient.Set(res.RefreshToken, user, 12*60*60); //12小时

                return Ok(res);
            }

            //登录失败，返回默认值 
            res.Code = (int)ResponseCode.LoginFail;
            res.Msg = ResponseCode.LoginFail.GetDescription();
            return Ok(res);
        }

        /// <summary>
        /// 刷新 访问token
        /// </summary>
        /// <param name="rtoken"></param>
        /// <returns></returns>
        [HttpGet]
        [AllowAnonymous]
        public IActionResult RefreshAccessToken(string rtoken, [FromServices] JwtTokenHelper _jwtTokenHelper)
        {
            UserLoginResponseDto res = new UserLoginResponseDto();
            //从Redis里面把用户信息读出来
            var user = _redisClient.Get<UserDto>(rtoken);
            if (user != null)
            {
                res.TokenInfo = _jwtTokenHelper.CreateToken(user);
                res.RefreshToken = rtoken;
                res.Code = 1;
                res.Msg = "刷新成功";
                res.Data = user; //用户信息
                return Ok(res);
            }
            else
            {
                res.Code = -1;
                res.Msg = "Token已过期，刷新失败";
                return Ok(res);
            }
        }

        /// <summary>
        /// 通过用户Id获取菜单
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        //[AllowAnonymous]
        public IActionResult GetMenus()
        {
            //获取用户主键
            var userIdClaim = HttpContext.User.FindFirst(s => s.Type == ClaimTypes.NameIdentifier);
            int userId;
            if (!int.TryParse(userIdClaim.Value, out userId))
            {
                return BadRequest();
            }

            // 第一个，先去redis中取该用户的菜单信息
            //如果没有该用户的菜单，则取数据库中获取

            //这里的key是角色的id
            var key = $"userId_{userId}_menu";
            //查询redis是否存入信息
            var value = _redisClient.Get(key);
            if (string.IsNullOrEmpty(value))
            {
                //访问数据库
                var list = _userService.GetUserMenu(userId);
                if (list == null)
                {
                    return Ok(new ResponseModel<object> 
                    { 
                        Code = (int)ResponseCode.Fail, 
                        Msg = "尚未给用户分配菜单",
                        Data = null 
                    });
                }
                
                //保存进Redis (1小时过期）
                _redisClient.Set(key, list, 1*3600);
            }

            //从redis里面取的数据需要反序列化为list集合
            var result = _redisClient.Get<List<PermissionTreeDto>>(key);

            return Ok(new ResponseModel<object>
            {
                Code = (int)ResponseCode.Success,
                Msg = ResponseCode.Success.GetDescription(),
                Data = result
            });
        }

    }
}
